// Two-Dimensional Sierpinski Gasket       
// Generated using randomly selected vertices and bisection

#include "Grammar.h"
#include "PLYModel.h"
#include "Turtle.h"

#define PI 3.14159265
//----------------------------------------------------------------------------
int width = 512;
int height = 512;

// remember to prototype
void generateGeometry( void );
void display( void );
void keyboard( unsigned char key, int x, int y );
void quad( int a, int b, int c, int d );
void colorcube(void);
void drawCube(void);
void myInit(void);
void drawTree(int treeIndex);

//typedef Angel::vec4  color4;
//typedef Angel::vec4  point4;

// handle to program
GLuint program;

using namespace std;

const int NumVertices = 36; //(6 faces)(2 triangles/face)(3 vertices/triangle)

point4 points[NumVertices];
color4 colors[NumVertices];

//grammar lsystem
const int NumOfGrammars = 5;
//Grammar grammars[treeIndex];
Grammar grammars[NumOfGrammars];
string lSystemFileFolder = "lSystemFiles\\";
string lSystemFiles[] = {"lsys1.txt","lsys2.txt","lsys3.txt","lsys4.txt","lsysTest.txt"};//need to change

stack<mat4> matrixStack;
mat4 planeCTM;

// Vertices of a unit cube centered at origin, sides aligned with axes
point4 vertices[8] = {
    point4( -0.5, -0.5,  0.5, 1.0 ),
    point4( -0.5,  0.5,  0.5, 1.0 ),
    point4(  0.5,  0.5,  0.5, 1.0 ),
    point4(  0.5, -0.5,  0.5, 1.0 ),
    point4( -0.5, -0.5, -0.5, 1.0 ),
    point4( -0.5,  0.5, -0.5, 1.0 ),
    point4(  0.5,  0.5, -0.5, 1.0 ),
    point4(  0.5, -0.5, -0.5, 1.0 )
};
//// RGBA olors
color4 vertex_colors[8] = {
    color4( 0.0, 0.0, 0.0, 1.0 ),  // black
    color4( 1.0, 0.0, 0.0, 1.0 ),  // red
    color4( 1.0, 1.0, 0.0, 1.0 ),  // yellow
    color4( 0.0, 1.0, 0.0, 1.0 ),  // green
    color4( 0.0, 0.0, 1.0, 1.0 ),  // blue
    color4( 1.0, 0.0, 1.0, 1.0 ),  // magenta
    color4( 1.0, 1.0, 1.0, 1.0 ),  // white
    color4( 0.0, 1.0, 1.0, 1.0 )   // cyan
};
// quad generates two triangles for each face and assigns colors
//    to the vertices
int Index = 0;
void quad( int a, int b, int c, int d )
{
    colors[Index] = vertex_colors[a]; points[Index] = vertices[a]; Index++;
    colors[Index] = vertex_colors[b]; points[Index] = vertices[b]; Index++;
    colors[Index] = vertex_colors[c]; points[Index] = vertices[c]; Index++;
    colors[Index] = vertex_colors[a]; points[Index] = vertices[a]; Index++;
    colors[Index] = vertex_colors[c]; points[Index] = vertices[c]; Index++;
    colors[Index] = vertex_colors[d]; points[Index] = vertices[d]; Index++;
}

// generate 12 triangles: 36 vertices and 36 colors
void colorcube()
{
    quad( 1, 0, 3, 2 );
    quad( 2, 3, 7, 6 );
    quad( 3, 0, 4, 7 );
    quad( 6, 5, 1, 2 );
    quad( 4, 5, 6, 7 );
    quad( 5, 4, 0, 1 );
}


Model3D sphere = Model3D();
Model3D cylinder = Model3D();
Model3D plane = Model3D();
Model3D ant = Model3D();

void myInit()
{
	//generate random
	srand((unsigned)time(0));

	//grammar init
	for(int index = 0;index<NumOfGrammars;index++)
	{
		Grammar grm = Grammar();
		grm.readFile(lSystemFileFolder+lSystemFiles[index]);
		grammars[index] = grm;
	}

	//init Model3D
	sphere.init("plyFiles\\sphere.ply","sphere");
	cylinder.init("plyFiles\\cylinder.ply","cylinder");
	plane.init("","plane");
	ant.init("plyFiles\\big_porsche.ply");
}



void drawTree(int treeIndex)
{
	//random position
	float randomX = -15.0f+rand()%30;
	float randomY = -20.0f+rand()%30;
	float randomZ = 0.0f;//-20.0f+rand()%40;
	point3 initPosition = point3(randomX,randomY,randomZ);

	

	//init ctm
	mat4 ctm = Angel::identity();
	mat4 camera = Angel::identity() * 
		Translate(initPosition)* 
		planeCTM *
		RotateY(90.0f)*
		Translate(-initPosition);

	ctm = ctm * Angel::Translate(initPosition);

	//random color
	color4 color = color4(rand()%10/10.0f,rand()%10/10.0f,rand()%10/10.0f,1.0f);

	//for 180 degree
	double theta1;
	double theta2;
	point3 dir;

	//clear stack
	while ( ! matrixStack.empty() )
	{
		matrixStack.pop();
	}
	//init turtle
	Turtle turtle = Turtle(grammars[treeIndex].rewrite());
	
	turtle.setPosition( initPosition);

	//start draw
	sphere.drawModel(camera * ctm,color);

	while(!turtle.isEnd())
	{
		char cmd = turtle.getCommand();
		switch(cmd){
		case 'F':
			//draw line
			cylinder.drawModel(camera * ctm, color);
			//move turtle
			turtle.forward(grammars[treeIndex].getLen());
			ctm = Angel::Translate(turtle.getDirection()*grammars[treeIndex].getLen())*ctm;
			//draw dot
			sphere.drawModel(camera * ctm,color);
			break;
		case 'f':
			//move turtle
			turtle.forward(grammars[treeIndex].getLen());
			ctm = Angel::Translate(turtle.getDirection()*grammars[treeIndex].getLen())*ctm;
			break;
		case '+'://+x
			//rotate turtle
			turtle.rotateX(grammars[treeIndex].getXRot());
			ctm =  Angel::Translate(turtle.getPosition()) 
				* Angel::RotateX( grammars[treeIndex].getXRot()) 
				* Angel::Translate(-turtle.getPosition()) *ctm ;
			break;
		case '-'://-x
			//rotate turtle
			turtle.rotateX(-grammars[treeIndex].getXRot());
			ctm =  Angel::Translate(turtle.getPosition()) 
				* Angel::RotateX( -grammars[treeIndex].getXRot()) 
				* Angel::Translate(-turtle.getPosition()) *ctm ;
			break;
		case '&'://+y
			//rotate turtle
			turtle.rotateY(grammars[treeIndex].getYRot());
			ctm =  Angel::Translate(turtle.getPosition()) 
				* Angel::RotateY( grammars[treeIndex].getYRot()) 
				* Angel::Translate(-turtle.getPosition()) *ctm ;
			break;
		case '^'://-y
			//rotate turtle
			turtle.rotateY(-grammars[treeIndex].getYRot());
			ctm =  Angel::Translate(turtle.getPosition()) 
				* Angel::RotateY(-grammars[treeIndex].getYRot()) 
				* Angel::Translate(-turtle.getPosition()) *ctm ;
			break;
		case '\\'://+z
			//rotate turtle
			turtle.rotateZ(grammars[treeIndex].getZRot());
			ctm =  Angel::Translate(turtle.getPosition()) 
				* Angel::RotateZ( grammars[treeIndex].getZRot()) 
				* Angel::Translate(-turtle.getPosition()) *ctm ;
			break;
		case '/'://-z
			//rotate turtle
			turtle.rotateZ(-grammars[treeIndex].getZRot());
			ctm =  Angel::Translate(turtle.getPosition()) 
				* Angel::RotateZ( -grammars[treeIndex].getZRot()) 
				* Angel::Translate(-turtle.getPosition()) *ctm ;
			break;
		case '|'://180
			//dir = turtle.getDirection();
			//theta1 = acos(dir.y) * 180 / PI;//d = 1
			//theta2 = atan(dir.z / dir.x) * 180 / PI;
			//ctm = Angel::Translate(turtle.getPosition()) 
			//	* Angel::RotateX(-theta1) * Angel::RotateY(theta2) 
			//	* Angel::RotateZ(180)
			//	* Angel::RotateY(-theta2) * Angel::RotateX(theta1)
			//	* Angel::Translate(-turtle.getPosition()) *ctm;
			//turtle.turnAround();
			break;
		case '['://push
			matrixStack.push(ctm);
			turtle.pushState();
			break;
		case ']'://pop
			ctm = matrixStack.top();
			matrixStack.pop();
			turtle.popState();
			break;
		}
	}
}

//----------------------------------------------------------------------------
// this is where the drawing should happen
void display( void )
{
    
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); 
	planeCTM = Angel::identity()*Translate(0.0f,0.0f,-40.0f)*RotateX(45);
	plane.drawModel(planeCTM);
	glutSwapBuffers();
}

//----------------------------------------------------------------------------
bool startDraw = true;

// keyboard handler
void keyboard( unsigned char key, int x, int y )
{
    switch ( key ) {
    case 033:
        exit( EXIT_SUCCESS );
        break;
	case 'A':
		glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
		plane.swapTexture();
		display();
		break;
	case 'a':
		if(startDraw){
		glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); 
		startDraw = false;
		}
		glutSwapBuffers();
		drawTree(0);//int index
		//glFlush(); 
		display();
		//glutSwapBuffers();
		break;
	case 'b':
		//glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); 
		if(startDraw){
		glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
		startDraw = false;
		}
		glutSwapBuffers();
		drawTree(1);//int index
		display();
		//glutSwapBuffers();
		break;
	case 'c':
		//glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); 
		if(startDraw){
		glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); 
		startDraw = false;
		}
		glutSwapBuffers();
		drawTree(2);//int index
		display();
		break;
	case 'd':
		if(startDraw){
		glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); 
		startDraw = false;
		}
		glutSwapBuffers();
		drawTree(3);//int index
		display();
		break;
	case 'e':
		if(startDraw){
		glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); 
		startDraw = false;
		}
		glutSwapBuffers();
		drawTree(4);//int index
		display();
		break;
	case 'f':
		glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); 
		//draw Park!
		startDraw = true;
		for(int index = 0;index<2;index++)
		{
			drawTree(rand()%5);
		}
		ant.drawModel( planeCTM * Scale(2.0,2.0,2.0));
		display();
		break;

    }
}



//----------------------------------------------------------------------------
// entry point
int main( int argc, char **argv )
{
	// init glut
    glutInit( &argc, argv );
    glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH );
    glutInitWindowSize( 1024,1024 );
    glutInitContextVersion( 3, 1 );
    glutInitContextProfile( GLUT_CORE_PROFILE );
	// create window
    glutCreateWindow( "Park" );


	// init glew
    glewInit();

	//my init
	myInit();
    

	// register callbacks
    glutDisplayFunc( display );
    glutKeyboardFunc( keyboard );
	
	//main loop
    glutMainLoop();
    return 0;
}
